/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef GrProcessorUnitTest_DEFINED
#define GrProcessorUnitTest_DEFINED

#include "include/core/SkTypes.h"

#if defined(GR_TEST_UTILS)

#include "include/private/base/SkTArray.h"
#include "src/base/SkArenaAlloc.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/GrTestUtils.h"
#include "src/gpu/ganesh/GrTextureProxy.h"

#include <tuple>

class GrCaps;
class GrFragmentProcessor;
class GrGeometryProcessor;
class GrProcessorTestData;
class GrProxyProvider;
class GrTexture;
class GrXPFactory;
class SkMatrix;

namespace GrProcessorUnitTest {
/* * This allows parent FPs to implement a test create with known leaf children in order to avoid
 * creating an unbounded FP tree which may overflow various shader limits.
 * MakeOptionalChildFP is the same as MakeChildFP, but can return null.
 */
std::unique_ptr<GrFragmentProcessor> MakeChildFP(GrProcessorTestData *);
std::unique_ptr<GrFragmentProcessor> MakeOptionalChildFP(GrProcessorTestData *);
} // namespace GrProcessorUnitTest

/* * GrProcessorTestData is an argument struct to TestCreate functions
 * fTextures are valid textures that can optionally be used to construct
 * TextureSampler. The first texture has a RGBA8 format and the second has Alpha8 format for the
 * specific backend API. TestCreate functions are also free to create additional textures using
 * the GrContext.
 */
class GrProcessorTestData {
public:
    using ViewInfo = std::tuple<GrSurfaceProxyView, GrColorType, SkAlphaType>;

    GrProcessorTestData(SkRandom *random, GrRecordingContext *context, int maxTreeDepth, int numViews,
        const ViewInfo views[]);
    GrProcessorTestData(SkRandom *random, GrRecordingContext *context, int maxTreeDepth, int numViews,
        const ViewInfo views[], std::unique_ptr<GrFragmentProcessor> inputFP);
    GrProcessorTestData(const GrProcessorTestData &) = delete;
    ~GrProcessorTestData();

    GrRecordingContext *context()
    {
        return fContext;
    }
    GrProxyProvider *proxyProvider();
    const GrCaps *caps();
    SkArenaAlloc *allocator()
    {
        return fArena.get();
    }
    std::unique_ptr<GrFragmentProcessor> inputFP();

    ViewInfo randomView();
    ViewInfo randomAlphaOnlyView();

    SkRandom *fRandom;
    int fCurrentTreeDepth = 0;
    int fMaxTreeDepth = 1;

private:
    GrRecordingContext *fContext;
    skia_private::TArray<ViewInfo> fViews;
    std::unique_ptr<SkArenaAlloc> fArena;
    std::unique_ptr<GrFragmentProcessor> fInputFP;
};

class GrProcessor;
class GrTexture;

template <class ProcessorSmartPtr> class GrProcessorTestFactory : private SkNoncopyable {
public:
    using MakeProc = ProcessorSmartPtr (*)(GrProcessorTestData *);

    GrProcessorTestFactory(MakeProc makeProc, const char *name);

    /* * Pick a random factory function and create a processor.  */
    static ProcessorSmartPtr Make(GrProcessorTestData *data);

    /* * Use factory function at Index idx to create a processor. */
    static ProcessorSmartPtr MakeIdx(int idx, GrProcessorTestData *data);

    /* * Number of registered factory functions */
    static int Count();

private:
    /* * A test function which verifies the count of factories. */
    static void VerifyFactoryCount();
    static skia_private::TArray<GrProcessorTestFactory<ProcessorSmartPtr> *, true> *GetFactories();

    MakeProc fMakeProc;
    SkString fName;
};

using GrFragmentProcessorTestFactory = GrProcessorTestFactory<std::unique_ptr<GrFragmentProcessor>>;
using GrGeometryProcessorTestFactory = GrProcessorTestFactory<GrGeometryProcessor *>;

class GrXPFactoryTestFactory : private SkNoncopyable {
public:
    using GetFn = const GrXPFactory *(GrProcessorTestData *);

    GrXPFactoryTestFactory(GetFn *getProc);

    static const GrXPFactory *Get(GrProcessorTestData *data);

private:
    /* * A test function which verifies the count of factories. */
    static void VerifyFactoryCount();
    static skia_private::TArray<GrXPFactoryTestFactory *, true> *GetFactories();

    GetFn *fGetProc;
};

#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS

/* * GrProcessor subclasses should insert this macro in their declaration to be included in the
 * program generation unit test.
 */
#define GR_DECLARE_GEOMETRY_PROCESSOR_TEST                                \
    [[maybe_unused]] static GrGeometryProcessorTestFactory *gTestFactory; \
    static GrGeometryProcessor *TestCreate(GrProcessorTestData *);

#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST                                \
    [[maybe_unused]] static GrFragmentProcessorTestFactory *gTestFactory; \
    static std::unique_ptr<GrFragmentProcessor> TestCreate(GrProcessorTestData *);

#define GR_DECLARE_XP_FACTORY_TEST                                \
    [[maybe_unused]] static GrXPFactoryTestFactory *gTestFactory; \
    static const GrXPFactory *TestGet(GrProcessorTestData *);

/* * GrProcessor subclasses should insert this macro in their implementation file. They must then
 * also implement this static function:
 * GrProcessor* TestCreate(GrProcessorTestData*);
 */
#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(Effect)          \
    GrFragmentProcessorTestFactory *Effect::gTestFactory = \
        new GrFragmentProcessorTestFactory(Effect::TestCreate, #Effect);

#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(Effect)          \
    GrGeometryProcessorTestFactory *Effect::gTestFactory = \
        new GrGeometryProcessorTestFactory(Effect::TestCreate, #Effect);

#define GR_DEFINE_XP_FACTORY_TEST(Factory) \
    GrXPFactoryTestFactory *Factory::gTestFactory = new GrXPFactoryTestFactory(Factory::TestGet);

#else
// !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS

// The unit test relies on static initializers. Just declare the TestCreate function so that
// its definitions will compile.
#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST \
    static std::unique_ptr<GrFragmentProcessor> TestCreate(GrProcessorTestData *);
#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(X)

// The unit test relies on static initializers. Just declare the TestCreate function so that
// its definitions will compile.
#define GR_DECLARE_GEOMETRY_PROCESSOR_TEST static GrGeometryProcessor *TestCreate(GrProcessorTestData *);
#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(X)

// The unit test relies on static initializers. Just declare the TestGet function so that
// its definitions will compile.
#define GR_DECLARE_XP_FACTORY_TEST const GrXPFactory *TestGet(GrProcessorTestData *);
#define GR_DEFINE_XP_FACTORY_TEST(X)

#endif
// !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
#else
// defined(GR_TEST_UTILS)
#define GR_DECLARE_GEOMETRY_PROCESSOR_TEST
#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST
#define GR_DECLARE_XP_FACTORY_TEST
#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(...)
#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(...)
#define GR_DEFINE_XP_FACTORY_TEST(...)
#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST
#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(...)
#define GR_DECLARE_GEOMETRY_PROCESSOR_TEST
#define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(...)
#define GR_DECLARE_XP_FACTORY_TEST
#define GR_DEFINE_XP_FACTORY_TEST(...)
#endif
// defined(GR_TEST_UTILS)
#endif
// GrProcessorUnitTest_DEFINED
